﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Xml.Linq;

using DbSharp.Framework.Assertion;
using DbSharp.Framework.DbObject;

namespace DbSharp.Framework.CaseFile.Parser
{
    /// <summary>
    /// Extract all DataTestCase objects from case file
    /// </summary>
    public class UnitTestCaseExtractor
    {
        #region Private members
        XElement m_GlobalSettingElement;
        XElement m_DataTestCasesRootElement;

        IEnumerable<IStatementDeclaration> m_ListStatement;
        List<UnitTestCase> m_ListCase = new List<UnitTestCase>();

        #endregion

        public UnitTestCaseExtractor(IEnumerable<IStatementDeclaration> listStatement, XElement globalSettingElement, XElement dataTestCasesRootElement)
        {
            if (dataTestCasesRootElement != null)
            {
                if ((listStatement == null) &&
                    (dataTestCasesRootElement.Elements("unitTestCase").Count() > 0))
                    throw new ArgumentNullException("listStatement");
            }
            m_ListStatement = listStatement;
            m_GlobalSettingElement = globalSettingElement;
            m_DataTestCasesRootElement = dataTestCasesRootElement;

            Extract();
        }


        #region Private methods


        private void Extract()
        {
            m_ListCase.Clear();
            if (m_DataTestCasesRootElement == null)
                return;

            bool logicNotMarkInAssertion;
            var q = from caseElement in m_DataTestCasesRootElement.Elements("unitTestCase")
                    let ignoreThisAttribute = caseElement.Attribute("ignoreThis")
                    let testInSandboxAttr = caseElement.Attribute("testInSandbox")
                    let remarkElement = caseElement.Element("testCaseRemark")
                    let tagsAttr = caseElement.Attribute("tags")
                    let assertionAttr = caseElement.Attribute("assertion")
                    let assertionMode =
                        assertionAttr == null ? new AssertionType
                        {
                            PrimitiveType = AssertPrimitiveType.StrictEqual,
                            WithLogicNotMark = false
                        } : new AssertionType
                        {
                            PrimitiveType = AssertionTypeHelper.Parse(assertionAttr.Value, out logicNotMarkInAssertion),
                            WithLogicNotMark = logicNotMarkInAssertion
                        }
                    select new
                    {
                        TestCaseName = caseElement.Attribute("testCaseName").Value.Trim(),
                        Assertion = assertionMode,
                        IgnoreThis = ignoreThisAttribute == null ? false : ignoreThisAttribute.Value.Trim().ToUpper() == "TRUE",
                        TestInSandbox = testInSandboxAttr == null ? false : testInSandboxAttr.Value.Trim().ToUpper() == "TRUE",
                        TestCaseRemark = remarkElement == null ? string.Empty : remarkElement.Value,
                        TagsValue = tagsAttr == null ? string.Empty : tagsAttr.Value,
                        TargetResultSetElement = caseElement.Element("targetResultset"),
                        ReferResultSetElement = caseElement.Element("referenceResultset"),
                        SetupElement = caseElement.Element("setup"),
                        TeardownElement = caseElement.Element("teardown")
                    };

            foreach (var item in q)
            {
                var caseObj = new UnitTestCase();
                var targetRsParser = new ResultsetSectionParser(item.TestCaseName, m_GlobalSettingElement,
                    m_ListStatement, item.TargetResultSetElement);
                caseObj.TargetResultsetSection = targetRsParser.ResultsetConfigSection;
                var referRsParser = new ResultsetSectionParser(item.TestCaseName, m_GlobalSettingElement,
                    m_ListStatement, item.ReferResultSetElement);
                caseObj.ReferResultsetSection = referRsParser.ResultsetConfigSection;
                caseObj.IgnoreThis = item.IgnoreThis;
                caseObj.TestCaseName = item.TestCaseName;
                caseObj.TestCaseRemark = item.TestCaseRemark;
                caseObj.Assertion = item.Assertion;
                caseObj.TestInSandbox = item.TestInSandbox;
                caseObj.TagsValue = item.TagsValue;

                var setupStatementExtractor = new SetupSectionExtractor(item.TestCaseName, m_GlobalSettingElement, m_ListStatement, item.SetupElement);
                foreach (var action in setupStatementExtractor.ListAction)
                    caseObj.AddSetupStatement(action);

                var teardownStatementExtractor = new SetupSectionExtractor(item.TestCaseName, m_GlobalSettingElement, m_ListStatement, item.TeardownElement);
                foreach (var action in teardownStatementExtractor.ListAction)
                    caseObj.AddTeardownStatment(action);

                m_ListCase.Add(caseObj);
            }
        }

        #endregion


        #region Public Properties/Methods

        public ReadOnlyCollection<UnitTestCase> ListCase
        {
            get { return new ReadOnlyCollection<UnitTestCase>(m_ListCase); }
        }

        public UnitTestCase this[string testCaseName]
        {
            get
            {
                if (string.IsNullOrEmpty(testCaseName))
                    return null;

                string upperCaseName = testCaseName.Trim().ToUpper();
                foreach (var item in m_ListCase)
                {
                    if (item.TestCaseName.Trim().ToUpper() == upperCaseName)
                        return item;
                }
                return null;
            }
        }


        public void TruncateTestCases(int maxCount)
        {
            int startIndex = maxCount ;
            if (startIndex > -1)
                m_ListCase.RemoveRange(startIndex, m_ListCase.Count);
        }


        #endregion
      

    }
}
